package cn.micro.core.util;

import java.io.Serializable;
import java.lang.invoke.SerializedLambda;
import java.lang.ref.WeakReference;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;
import java.util.function.Supplier;

/**
 * 对象转换工具
 * 支持源对象到目标对象的属性映射，追踪字段变更、记录需要清空的字段
 *
 * @param <T> 源对象
 * @param <R> 目标对象
 */
public final class ObjectConvert<T, R> {

    private static final Map<Class<?>, WeakReference<LambdaMeta>> LAMBDA_CACHE = new ConcurrentHashMap<>();

    /**
     * 字段名缓存：键为 "类名::方法名"，值为解析后的字段名（复用解析结果）
     */
    private static final Map<String, String> FIELD_NAME_CACHE = new ConcurrentHashMap<>();

    private final AtomicBoolean lazy = new AtomicBoolean(false);

    private Map<String, LazySetterValue<R, ?>> modifiedColumn;

    private T source;

    private R target;

    private R changeR;

    private ObjectConvert(T source, R target, R changeR) {
        this.source = source;
        this.target = target;
        this.changeR = changeR;
    }

    public static <T, R> ObjectConvert<T, R> wrap(R target) {
        return new ObjectConvert<>(null, target, null);
    }

    public static <T, R> ObjectConvert<T, R> wrap(Supplier<R> target) {
        return new ObjectConvert<>(null, target.get(), null);
    }

    public Target<T, R> ops() {
        this.checkTarget();
        return new Target<>(this);
    }

    public TargetChange<T, R> ops(Supplier<R> changeR) {
        this.changeR = Optional.ofNullable(changeR).map(Supplier::get).orElse(null);
        this.checkTarget();
        this.checkChangeTarget();
        this.modifiedColumn = new ConcurrentHashMap<>();
        return new TargetChange<>(this);
    }

    @SuppressWarnings("unchecked")
    public <S extends T> SourceTarget<S, R> ops(S source) {
        this.source = source;
        this.checkSource();
        this.checkTarget();
        return (SourceTarget<S, R>) new SourceTarget<>(this);
    }

    @SuppressWarnings("unchecked")
    public <S extends T> SourceTargetChange<S, R> ops(S source, Supplier<R> changeR) {
        this.source = source;
        this.changeR = Optional.ofNullable(changeR).map(Supplier::get).orElse(null);
        this.checkSource();
        this.checkTarget();
        this.checkChangeTarget();
        this.modifiedColumn = new ConcurrentHashMap<>();
        return (SourceTargetChange<S, R>) new SourceTargetChange<>(this);
    }

    private void lazy() {
        this.lazy.compareAndSet(false, true);
    }

    private void build() {
        if (this.lazy.get() &&
                this.modifiedColumn != null &&
                !this.modifiedColumn.isEmpty() &&
                this.target != this.changeR) {
            for (Map.Entry<String, LazySetterValue<R, ?>> entry : this.modifiedColumn.entrySet()) {
                LazySetterValue<R, ?> lazySetterValue = entry.getValue();
                lazySetterValue.accept(this.target);
            }
        }
    }

    private void checkTarget() {
        if (null == this.target) {
            this.target = this.changeR;
        }
        Objects.requireNonNull(this.target, "目标对象不能为null");
    }

    private void checkSource() {
        Objects.requireNonNull(this.source, "源对象不能为null");
    }

    private void checkChangeTarget() {
        Objects.requireNonNull(this.changeR, "目标修改对象不能为null");
    }

    /**
     * 转换设置列新值（从源对象获取新值）
     *
     * @param sourceGetter 新值提供者（源对象的 getter 方法引用）
     * @param targetSetter 新值设置者（目标对象的 setter 方法引用）
     * @param <V>          新值类型
     * @param <P1>         旧值类型
     * @return 转换器实例（链式调用）
     */
    private <V extends P1, P1> ObjectConvert<T, R> convert(Getter<T, V> sourceGetter,
                                                           Setter<R, P1> targetSetter) {
        V newValue = sourceGetter.apply(source);
        convert(targetSetter, newValue);
        return this;
    }

    /**
     * 转换设置列新值（直接传入固定新值）
     *
     * @param targetSetter 新值设置者（目标对象的 setter 方法引用）
     * @param v            固定新值
     * @param <V>          新值类型
     * @param <P1>         旧值类型
     * @return 转换器实例（链式调用）
     */
    private <V extends P1, P1> ObjectConvert<T, R> convert(Setter<R, P1> targetSetter,
                                                           V v) {
        targetSetter.accept(target, v);
        return this;
    }

    /**
     * 转换设置列新值（追踪旧值变化，记录需要清空的字段）
     *
     * @param sourceGetter 新值提供者（源对象的 getter 方法引用）
     * @param targetGetter 旧值提供者（目标对象的 getter 方法引用，用于对比新旧值）追踪旧值变化
     * @param targetSetter 新值设置者（目标对象的 setter 方法引用）
     * @param <V>          新值类型
     * @param <P1>         旧值类型
     * @return 转换器实例（链式调用）
     */
    private <V extends P1, P1> ObjectConvert<T, R> convert(Getter<T, V> sourceGetter,
                                                           Getter<R, P1> targetGetter,
                                                           Setter<R, P1> targetSetter) {
        V newValue = sourceGetter.apply(source);
        this.convert(targetGetter, targetSetter, newValue);
        return this;
    }

    /**
     * 转换并设置列新值（追踪旧值变化，记录需要清空的字段）
     *
     * @param targetGetter 旧值提供者（通常是目标对象的 getter 方法引用，用于对比新旧值）
     * @param targetSetter 新值设置者（通常是目标对象的 setter 方法引用）
     * @param v            新值（来自源对象或自定义值）
     * @param <V>          新值类型
     * @param <P1>         旧值类型
     * @return 转换器实例（支持链式调用）
     */
    private <V extends P1, P1> ObjectConvert<T, R> convert(Getter<R, P1> targetGetter,
                                                           Setter<R, P1> targetSetter,
                                                           V v) {
        P1 oldValue = targetGetter.apply(target);
        P1 p1 = this.solveColumnValue(getFieldName(targetGetter), oldValue, v, targetSetter);
        if (null != p1) {
            targetSetter.accept(changeR, p1);
        }
        if (!this.lazy.get() && this.target != this.changeR) {
            convert(targetSetter, v);
        }
        return this;
    }

    private T getSource() {
        return this.source;
    }

    private R getTarget() {
        return this.target;
    }

    private boolean isModified() {
        return null != this.modifiedColumn && !this.modifiedColumn.isEmpty();
    }

    private boolean isFieldModified(String fieldName) {
        return this.modifiedColumn.containsKey(fieldName);
    }

    private R getChangeTarget() {
        return this.changeR;
    }

    private Map<String, Boolean> getClearColumn() {
        if (!isModified()) {
            return Collections.emptyMap();
        }
        Map<String, Boolean> clearColumn = new HashMap<>(this.modifiedColumn.size());
        for (Map.Entry<String, LazySetterValue<R, ?>> entry : this.modifiedColumn.entrySet()) {
            if (entry.getValue().isClear()) {
                clearColumn.put(entry.getKey(), Boolean.TRUE);
            }
        }
        return clearColumn;
    }

    /**
     * 通过 Lambda 方法引用获取字段名（核心方法，高性能解析）
     *
     * @param getter Lambda 方法引用（通常是 getter 方法引用）
     * @param <T>    源对象类型
     * @param <R>    字段值类型
     * @return 解析后的字段名（符合 JavaBean 规范）
     */
    private static <T, R> String getFieldName(Getter<T, R> getter) {
        Class<?> lambdaClass = getter.getClass();
        try {
            LambdaMeta lambdaMeta = Optional.ofNullable(LAMBDA_CACHE.get(lambdaClass))
                    .map(WeakReference::get)
                    .orElse(null);
            if (null == lambdaMeta) {
                Method writeReplaceMethod = getter.getClass().getDeclaredMethod("writeReplace");
                writeReplaceMethod.setAccessible(true);
                Object result = writeReplaceMethod.invoke(getter);
                if (!(result instanceof SerializedLambda)) {
                    throw new IllegalArgumentException("Lambda 表达式未生成 SerializedLambda");
                }
                SerializedLambda serializedLambda = (SerializedLambda) result;
                if (!lambdaClass.isSynthetic()) {
                    throw new IllegalArgumentException("参数必须是 Lambda 表达式，而非普通类实例");
                }
                lambdaMeta = new LambdaMeta(serializedLambda.getImplClass(), serializedLambda.getImplMethodName());
                LAMBDA_CACHE.put(lambdaClass, new WeakReference<>(lambdaMeta));
            }
            String cacheKey = lambdaMeta.getCacheKey();
            String methodName = lambdaMeta.getMethodName();
            return FIELD_NAME_CACHE.computeIfAbsent(cacheKey, key -> {
                if (methodName.startsWith("get")) {
                    return deCapitalize(methodName.substring(3));
                } else if (methodName.startsWith("is")) {
                    return deCapitalize(methodName.substring(2));
                } else {
                    throw new IllegalArgumentException("方法引用必须是getter方法（以get或is开头）");
                }
            });
        } catch (Throwable e) {
            throw new RuntimeException("解析 Lambda 表达式获取字段名失败，Lambda 类：" + lambdaClass.getName(), e);
        }
    }

    private static String deCapitalize(String name) {
        if (name == null || name.isEmpty()) {
            return name;
        }
        char firstChar = name.charAt(0);
        if (Character.isUpperCase(firstChar)) {
            if (name.length() > 1 && !Character.isUpperCase(name.charAt(1))) {
                return Character.toLowerCase(firstChar) + name.substring(1);
            } else {
                return name.toLowerCase();
            }
        }
        return name;
    }

    /**
     * 解析获取列值
     *
     * @param columnName 列名
     * @param dbValue    数据库列值
     * @param cmdValue   命令列值
     * @param <P>        列值类型
     * @return 列值
     */
    private <P> P solveColumnValue(String columnName,
                                   P dbValue,
                                   P cmdValue,
                                   Setter<R, P> targetSetter) {
        if (this.valuesAreEqual(dbValue, cmdValue)) {
            this.modifiedColumn.remove(columnName); // 相等时移除键
            return null;
        }
        this.modifiedColumn.put(columnName, new LazySetterValue<>(targetSetter,
                cmdValue,
                this.lazy.get(),
                (null != dbValue && cmdValue == null)));
        return cmdValue;
    }

    /**
     * 统一判断两个值是否相等（处理普通对象和BigDecimal特殊情况）
     *
     * @param dbValue  数据库列值
     * @param cmdValue 命令列值
     * @param <P>      列值类型
     * @return 是否相等
     */
    private <P> boolean valuesAreEqual(P dbValue, P cmdValue) {
        if (Objects.equals(dbValue, cmdValue)) {
            return true;
        }
        if (dbValue instanceof BigDecimal || cmdValue instanceof BigDecimal) {
            BigDecimal bdDb = toBigDecimal(dbValue);
            BigDecimal bdCmd = toBigDecimal(cmdValue);
            return equalsByBigDecimal(bdDb, bdCmd);
        }
        return false;
    }

    /**
     * BigDecimal 转换
     *
     * @param obj 对象
     * @return BigDecimal
     */
    private static BigDecimal toBigDecimal(Object obj) {
        if (obj == null) return null;
        if (obj instanceof BigDecimal) return (BigDecimal) obj;
        try {
            return new BigDecimal(obj.toString());
        } catch (NumberFormatException ignore) {
            return null;
        }
    }

    /**
     * BigDecimal 相等比较
     *
     * @param a 参数 a
     * @param b 参数 b
     * @return 是否相等
     */
    private static boolean equalsByBigDecimal(BigDecimal a, BigDecimal b) {
        if (a == null && b == null) {
            return true;
        }
        if (a == null || b == null) {
            return false;
        }
        return a.compareTo(b) == 0;
    }

    @FunctionalInterface
    public interface Getter<T, R> extends Function<T, R>, Serializable {

    }

    @FunctionalInterface
    public interface Setter<T, P1> extends Serializable {
        void accept(T t, P1 p1);
    }


    private static class LambdaMeta {
        private final String cacheKey;
        private final String methodName;

        public LambdaMeta(String implClass, String implMethodName) {
            String className = implClass.replace('/', '.');
            this.cacheKey = className + "::" + implMethodName;
            this.methodName = implMethodName;
        }

        public String getCacheKey() {
            return this.cacheKey;
        }

        public String getMethodName() {
            return this.methodName;
        }
    }

    private static class LazySetterValue<R, P> {
        private final Setter<R, P> setter;
        private final P value;
        private boolean lazy;
        private final boolean clear;

        public LazySetterValue(Setter<R, P> setter,
                               P value,
                               boolean lazy,
                               boolean clear) {
            this.setter = setter;
            this.value = value;
            this.lazy = lazy;
            this.clear = clear;
        }

        public Setter<R, P> getSetter() {
            return setter;
        }

        public P getValue() {
            return value;
        }

        public boolean isClear() {
            return clear;
        }

        public void accept(R r) {
            if (this.lazy) {
                setter.accept(r, value);
                this.lazy = false;
            }
        }
    }

    public static class Target<T, R> {

        private final ObjectConvert<T, R> convert;

        private Target(ObjectConvert<T, R> convert) {
            this.convert = convert;
        }

        public <V extends P1, P1> Target<T, R> convert(Setter<R, P1> targetSetter,
                                                       V v) {
            this.convert.convert(targetSetter, v);
            return this;
        }

        public R getTarget() {
            return this.convert.getTarget();
        }

    }

    public static class TargetChange<T, R> {

        private final ObjectConvert<T, R> convert;

        private TargetChange(ObjectConvert<T, R> convert) {
            this.convert = convert;
        }

        public <V extends P1, P1> TargetChange<T, R> lazy() {
            this.convert.lazy();
            return this;
        }

        public R build() {
            this.convert.build();
            return this.getTarget();
        }

        public <V extends P1, P1> TargetChange<T, R> convert(Getter<R, P1> targetGetter,
                                                             Setter<R, P1> targetSetter,
                                                             V v) {
            this.convert.convert(targetGetter, targetSetter, v);
            return this;
        }

        public R getTarget() {
            return this.convert.getTarget();
        }

        public boolean isModified() {
            return this.convert.isModified();
        }

        public <P1> boolean isFieldModified(Getter<R, P1> targetGetter) {
            return this.convert.isFieldModified(getFieldName(targetGetter));
        }

        public R getChangeTarget() {
            return this.convert.getChangeTarget();
        }

        public Map<String, Boolean> getClearColumn() {
            return this.convert.getClearColumn();
        }

    }

    public static class SourceTarget<T, R> {

        private final ObjectConvert<T, R> convert;

        private SourceTarget(ObjectConvert<T, R> convert) {
            this.convert = convert;
        }

        public <V extends P1, P1> SourceTarget<T, R> convert(Getter<T, V> sourceGetter,
                                                             Setter<R, P1> targetSetter) {
            this.convert.convert(sourceGetter, targetSetter);
            return this;
        }

        public <V extends P1, P1> SourceTarget<T, R> convert(Setter<R, P1> targetSetter,
                                                             V v) {
            this.convert.convert(targetSetter, v);
            return this;
        }

        public T getSource() {
            return this.convert.getSource();
        }

        public R getTarget() {
            return this.convert.getTarget();
        }

    }

    public static class SourceTargetChange<T, R> {
        private final ObjectConvert<T, R> convert;

        private SourceTargetChange(ObjectConvert<T, R> convert) {
            this.convert = convert;
        }

        public <V extends P1, P1> SourceTargetChange<T, R> lazy() {
            this.convert.lazy();
            return this;
        }

        public R build() {
            this.convert.build();
            return this.getTarget();
        }

        public <V extends P1, P1> SourceTargetChange<T, R> convert(Getter<T, V> sourceGetter,
                                                                   Getter<R, P1> targetGetter,
                                                                   Setter<R, P1> targetSetter) {
            this.convert.convert(sourceGetter, targetGetter, targetSetter);
            return this;
        }

        public <V extends P1, P1> SourceTargetChange<T, R> convert(Getter<R, P1> targetGetter,
                                                                   Setter<R, P1> targetSetter,
                                                                   V v) {
            this.convert.convert(targetGetter, targetSetter, v);
            return this;
        }

        public T getSource() {
            return this.convert.getSource();
        }

        public R getTarget() {
            return this.convert.getTarget();
        }

        public boolean isModified() {
            return this.convert.isModified();
        }

        public boolean isFieldModified(Getter<T, R> targetGetter) {
            return this.convert.isFieldModified(getFieldName(targetGetter));
        }

        public R getChangeTarget() {
            return this.convert.getChangeTarget();
        }

        public Map<String, Boolean> getClearColumn() {
            return this.convert.getClearColumn();
        }
    }

}
